package com.ruyuan.user.server.controller;

import com.ruyuan.common.model.Features;
import com.ruyuan.common.model.PageDTO;
import com.ruyuan.common.web.model.WebResponse;
import com.ruyuan.gateway.sdk.facade.RuyuanGatewayFacade;
import com.ruyuan.gateway.sdk.model.UserAuthorities;
import com.ruyuan.user.api.model.constants.UserAccountInfoExtendKeys;
import com.ruyuan.user.api.model.enums.AuthResourceTypeEnum;
import com.ruyuan.user.server.controller.converter.WebAuthorityConverter;
import com.ruyuan.user.server.controller.converter.WebRoleConverter;
import com.ruyuan.user.server.controller.request.*;
import com.ruyuan.user.server.controller.response.DataResourceVO;
import com.ruyuan.user.server.controller.response.FunctionResourceTreeVO;
import com.ruyuan.user.server.controller.response.RoleListVO;
import com.ruyuan.user.server.domain.model.AuthResource;
import com.ruyuan.user.server.domain.model.AuthResourceFeaturesKey;
import com.ruyuan.user.server.domain.model.Authority;
import com.ruyuan.user.server.domain.model.Role;
import com.ruyuan.user.server.domain.service.AuthorityDomainService;
import com.ruyuan.user.server.domain.service.RoleDomainService;
import com.ruyuan.user.server.domain.service.request.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.ruyuan.user.api.model.enums.AuthResourceTypeEnum.API;

/**
 * @author xx
 */
@Controller
@RequestMapping("/api/user/role")
public class RoleController {

    @Autowired
    private WebRoleConverter converter;

    @Autowired
    private WebAuthorityConverter authorityConverter;

    @Autowired
    private RoleDomainService roleDomainService;

    @Autowired
    private RuyuanGatewayFacade ruyuanGatewayFacade;

    @Autowired
    private AuthorityDomainService authorityDomainService;

    @RequestMapping("/currentUserFunction")
    @ResponseBody
    public WebResponse<List<FunctionResourceTreeVO>> currentUserFunction() {
        Long userId = ruyuanGatewayFacade.getCurrentUser().getUserId();
        try {
            // 查出当前用户的所有功能权限
            QueryUserAuthoritiesParam param = new QueryUserAuthoritiesParam();
            param.setUserId(userId);
            param.setTypeEnumList(Arrays.asList(AuthResourceTypeEnum.MENU, AuthResourceTypeEnum.OPERATION));
            List<Authority> authorities = roleDomainService.queryUserAllAuthorities(param);
            List<Long> allResourceIds = authorities.stream().map(Authority::getAuthResourceId).collect(Collectors.toList());
            // 查询功能资源
            Long orgId = Long.valueOf(ruyuanGatewayFacade.getCurrentUser().getExtendInfo().get(UserAccountInfoExtendKeys.ROOT_ORG_ID));
            PageQueryAuthResourceReq req = new PageQueryAuthResourceReq();
            req.setPageSize(Integer.MAX_VALUE);
            req.setCurrent(1);
            req.setOrgId(orgId);
            req.setAuthResourceTypes(Arrays.asList(AuthResourceTypeEnum.MENU, AuthResourceTypeEnum.OPERATION));
            PageDTO<AuthResource> resPage = authorityDomainService.queryAuthResourceByCondition(req);
            List<AuthResource> authResources = resPage.getData();
            if (CollectionUtils.isEmpty(allResourceIds)) {
                return WebResponse.successWithData(new ArrayList<>());
            }
            // 根据权限过滤功能资源，将禁用的菜单功能不显示
            List<AuthResource> enableAuthResources = authResources.stream().filter(authResource -> allResourceIds.contains(authResource.getId()) &&
                            Boolean.parseBoolean(Optional.ofNullable(authResource.getFeatures()).orElse(Features.of()).getFeature(AuthResourceFeaturesKey.ENABLE)))
                    .collect(Collectors.toList());
            return WebResponse.successWithData(authorityConverter.convertModelToTreeVO(enableAuthResources));
        } finally {
            reportUserAuthority(userId);
        }
    }

    private void reportUserAuthority(Long userId) {
        QueryUserAuthoritiesParam queryUserApiAuthoritiesParam = new QueryUserAuthoritiesParam();
        queryUserApiAuthoritiesParam.setUserId(userId);
        queryUserApiAuthoritiesParam.setTypeEnumList(Arrays.asList(API));
        List<Authority> apiAuthorities = roleDomainService.queryUserAllAuthorities(queryUserApiAuthoritiesParam);
        UserAuthorities userAuthorities = new UserAuthorities();
        userAuthorities.setUserId(userId);
        userAuthorities.setAuthorityCodes(Optional.ofNullable(apiAuthorities).orElse(new ArrayList<>()).stream()
                .map(Authority::getCode).collect(Collectors.toSet()));
        ruyuanGatewayFacade.reportUserAuthorities(userAuthorities);
    }

    @RequestMapping("/save")
    @ResponseBody
    public WebResponse<Long> saveRole(@Validated @RequestBody SaveRoleRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return WebResponse.failWithMsg(bindingResult.getFieldError().getDefaultMessage());
        }
        Role role = converter.convertSaveRequestToModel(request);
        Long roleId = roleDomainService.saveRole(role);
        return WebResponse.successWithData(roleId);
    }

    @RequestMapping("/del")
    @ResponseBody
    public WebResponse<Boolean> delRole(@Validated @RequestBody DelRoleRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return WebResponse.failWithMsg(bindingResult.getFieldError().getDefaultMessage());
        }
        Role role = roleDomainService.queryById(request.getRoleId());
        if (role == null) {
            return WebResponse.failWithMsg("角色不存在");
        }
        role.setModifier(String.valueOf(ruyuanGatewayFacade.getCurrentUser().getUserId()));
        role.setIsDeleted(true);
        roleDomainService.saveRole(role);
        return WebResponse.successWithData(true);
    }

    @GetMapping("/queryList")
    @ResponseBody
    public WebResponse<PageDTO<RoleListVO>> queryRoleList(RolePageQueryRequest request) {
        PageQueryRoleParam pageQueryRoleParam = new PageQueryRoleParam();
        if (StringUtils.isNotBlank(request.getName())) {
            pageQueryRoleParam.setName(request.getName());
        }
        if (StringUtils.isNotBlank(request.getCode())) {
            pageQueryRoleParam.setCode(request.getCode());
        }
        pageQueryRoleParam.setOrgId(Long.valueOf(ruyuanGatewayFacade.getCurrentUser().getExtendInfo().get(UserAccountInfoExtendKeys.ROOT_ORG_ID)));
        pageQueryRoleParam.setCurrent(request.getCurrent());
        pageQueryRoleParam.setPageSize(request.getPageSize());
        PageDTO<Role> rolePageDTO = roleDomainService.queryRoleByPage(pageQueryRoleParam);
        PageDTO<RoleListVO> res = PageDTO.of(rolePageDTO, rolePageDTO.getData().stream().map(converter::convertModelToListVO).collect(Collectors.toList()));
        return WebResponse.successWithData(res);
    }

    //给角色赋予功能资源权限
    @RequestMapping("/grantFunctionResource")
    @ResponseBody
    public WebResponse<Boolean> grantFunctionResourceToRole(@Validated @RequestBody GrantFunctionResourceRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return WebResponse.failWithMsg(bindingResult.getFieldError().getDefaultMessage());
        }
        // converter的功能是根据传入的资源集筛选出来需要新增的资源集对应的权限集
        innerSaveRoleAuthority(request.getRoleId(), Arrays.asList(AuthResourceTypeEnum.MENU, AuthResourceTypeEnum.OPERATION, AuthResourceTypeEnum.API), allFunctionResources ->
                allFunctionResources.stream().filter(authResource -> {
                    // 直接指定了当前资源
                    if (request.getResourceIdList().contains(authResource.getId())) {
                        return true;
                    }
                    // 间接指定资源
                    if (authResource.getType().getCode().equals(AuthResourceTypeEnum.API.getCode())) {
                        //API资源关联的功能资源id
                        String relatedIds = authResource.getFeatures().getFeature(AuthResourceFeaturesKey.RELATED_FUNCTION_RESOURCE_IDS);
                        if (StringUtils.isNotBlank(relatedIds)) {
                            String[] relatedIdArr = relatedIds.split(",");
                            return Arrays.stream(relatedIdArr).anyMatch(resourceId -> request.getResourceIdList().contains(Long.valueOf(resourceId)));
                        }
                    }
                    return false;
                }).map(authResource -> authResource.getRelatedAuthorities().get(0)).collect(Collectors.toList()));
        return WebResponse.successWithData(true);
    }

    //给角色赋予数据权限
    @RequestMapping("/grantDataAuthority")
    @ResponseBody
    public WebResponse<Boolean> grantDataAuthorityToRole(@Validated @RequestBody GrantDataAuthorityRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return WebResponse.failWithMsg(bindingResult.getFieldError().getDefaultMessage());
        }
        innerSaveRoleAuthority(request.getRoleId(), Arrays.asList(AuthResourceTypeEnum.DATA), allFunctionResources ->
                allFunctionResources.stream().map(AuthResource::getRelatedAuthorities).flatMap(Collection::stream)
                        .filter(authority -> request.getAuthorityIdList().contains(authority.getId())).collect(Collectors.toList()));
        return WebResponse.successWithData(true);
    }

    //查询角色拥有的功能资源集
    @GetMapping("/functionResourceList")
    @ResponseBody
    public WebResponse<List<Long>> queryRoleFunctionResourceList(Long roleId) {
        PageQueryRoleAuthoritiesParam param = new PageQueryRoleAuthoritiesParam();
        param.setRoleId(roleId);
        param.setTypeEnumList(Arrays.asList(AuthResourceTypeEnum.OPERATION, AuthResourceTypeEnum.MENU));
        param.setCurrent(1);
        param.setPageSize(Integer.MAX_VALUE);
        PageDTO<Authority> pageRes = roleDomainService.queryRoleAuthorities(param);
        List<Authority> authorityList = pageRes.getData();
        if (CollectionUtils.isEmpty(authorityList)) {
            return WebResponse.successWithData(new ArrayList<>());
        }
        List<Long> resourceIds = authorityList.stream().map(Authority::getAuthResourceId).collect(Collectors.toList());
        return WebResponse.successWithData(resourceIds);
    }

    //查询角色拥有的功能资源集
    @GetMapping("/dataResourceList")
    @ResponseBody
    public WebResponse<List<DataResourceVO>> queryRoleDataResourceList(Long roleId) {
        Long orgId = Long.valueOf(ruyuanGatewayFacade.getCurrentUser().getExtendInfo().get(UserAccountInfoExtendKeys.ROOT_ORG_ID));
        PageQueryAuthResourceReq req = new PageQueryAuthResourceReq();
        req.setOrgId(orgId);
        req.setAuthResourceTypes(Arrays.asList(AuthResourceTypeEnum.DATA));
        req.setCurrent(1);
        req.setPageSize(Integer.MAX_VALUE);
        PageDTO<AuthResource> pageRes = authorityDomainService.queryAuthResourceByCondition(req);
        List<AuthResource> authResourceList = pageRes.getData();
        if (CollectionUtils.isEmpty(authResourceList)) {
            return WebResponse.successWithData(new ArrayList<>());
        }
        // 转换成vo对象
        List<Authority> roleAuthorities = getRoleAuthorities(roleId, Arrays.asList(AuthResourceTypeEnum.DATA));
        List<DataResourceVO> dataResourceVOS = authorityConverter.convertModelToRoleDataResourceVO(authResourceList, roleAuthorities.stream().map(Authority::getId).collect(Collectors.toList()));
        return WebResponse.successWithData(dataResourceVOS);
    }


    //保存角色与权限的关系
    private void innerSaveRoleAuthority(Long roleId, List<AuthResourceTypeEnum> typeEnumList, Function<List<AuthResource>, List<Authority>> newAuthorityConverter) {
        Long orgId = Long.valueOf(ruyuanGatewayFacade.getCurrentUser().getExtendInfo().get(UserAccountInfoExtendKeys.ROOT_ORG_ID));
        String userId = ruyuanGatewayFacade.getCurrentUser().getUserId().toString();
        // 先查出当前角色所有的功能权限和API权限
        List<Authority> currentRoleAuthorities = getRoleAuthorities(roleId, typeEnumList);
        List<Long> currentRoleAuthorityIds = currentRoleAuthorities.stream().map(Authority::getId).collect(Collectors.toList());
        // 将入参的资源id转换成权限id，查出全部的功能资源和api资源
        PageQueryAuthResourceReq pageQueryAuthResourceRequest = new PageQueryAuthResourceReq();
        pageQueryAuthResourceRequest.setAuthResourceTypes(typeEnumList);
        pageQueryAuthResourceRequest.setOrgId(orgId);
        pageQueryAuthResourceRequest.setCurrent(1);
        pageQueryAuthResourceRequest.setPageSize(Integer.MAX_VALUE);
        PageDTO<AuthResource> allFunctionResourcesPage = authorityDomainService.queryAuthResourceByCondition(pageQueryAuthResourceRequest);
        List<AuthResource> allFunctionResources = allFunctionResourcesPage.getData();
        // 获得新的角色权限列表 数据资源和功能资源的逻辑不一致，我们就需要两个方法分别提供一个逻辑来进行判断
        List<Authority> newRoleAuthorities = newAuthorityConverter.apply(allFunctionResources);
        List<Long> newRoleAuthorityIds = newRoleAuthorities.stream().map(Authority::getId).collect(Collectors.toList());
        //找出要删除的角色与权限关联关系
        List<Authority> authorityToDelete = currentRoleAuthorities.stream().filter(authority -> !newRoleAuthorityIds.contains(authority.getId())).collect(Collectors.toList());
        ChangeRoleAuthorityParam moveParam = new ChangeRoleAuthorityParam();
        moveParam.setOperator(userId);
        moveParam.setAuthorityList(authorityToDelete);
        moveParam.setRoleId(roleId);
        roleDomainService.removeRoleAuthorities(moveParam);
        //找出要新增的角色与权限关联关系
        List<Authority> authorityToAdd = newRoleAuthorities.stream().filter(authority -> !currentRoleAuthorityIds.contains(authority.getId())).collect(Collectors.toList());
        ChangeRoleAuthorityParam addParam = new ChangeRoleAuthorityParam();
        addParam.setOperator(userId);
        addParam.setAuthorityList(authorityToAdd);
        addParam.setRoleId(roleId);
        roleDomainService.addRoleAuthorities(addParam);
    }

    private List<Authority> getRoleAuthorities(Long roleId, List<AuthResourceTypeEnum> typeEnumList) {
        PageQueryRoleAuthoritiesParam pageQueryRoleAuthoritiesParam = new PageQueryRoleAuthoritiesParam();
        pageQueryRoleAuthoritiesParam.setRoleId(roleId);
        // 资源类型作为参数传进来
        pageQueryRoleAuthoritiesParam.setTypeEnumList(typeEnumList);
        pageQueryRoleAuthoritiesParam.setCurrent(1);
        pageQueryRoleAuthoritiesParam.setPageSize(Integer.MAX_VALUE);
        PageDTO<Authority> resPage = roleDomainService.queryRoleAuthorities(pageQueryRoleAuthoritiesParam);
        return resPage.getData();
    }

    //给用户绑定角色
    @RequestMapping("/grantUser")
    @ResponseBody
    public WebResponse<Boolean> grantUserRole(@Validated @RequestBody GrantUserRoleRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return WebResponse.failWithMsg(bindingResult.getFieldError().getDefaultMessage());
        }
        Long userId = ruyuanGatewayFacade.getCurrentUser().getUserId();
        // 查询当前用户所有角色
        List<Role> currentUserRoles = roleDomainService.queryUserRole(request.getUserId());
        List<Long> currentUserRoleIds = currentUserRoles.stream().map(Role::getId).collect(Collectors.toList());
        // 删除用户角色
        List<Long> delIds = currentUserRoleIds.stream().filter(id -> !request.getRoleIdList().contains(id)).collect(Collectors.toList());
        ChangeUserRoleParam delParam = new ChangeUserRoleParam();
        delParam.setOperator(String.valueOf(userId));
        delParam.setUserId(request.getUserId());
        delParam.setRoleIdList(delIds);
        roleDomainService.removeUserRole(delParam);
        // 新增用户角色
        List<Long> addIds = request.getRoleIdList().stream().filter(roleId -> !currentUserRoleIds.contains(roleId)).collect(Collectors.toList());
        ChangeUserRoleParam addParam = new ChangeUserRoleParam();
        addParam.setOperator(String.valueOf(userId));
        addParam.setUserId(request.getUserId());
        addParam.setRoleIdList(addIds);
        roleDomainService.addUserRole(addParam);
        return WebResponse.successWithData(true);
    }

    //给岗位绑定角色
    @RequestMapping("/grantJob")
    @ResponseBody
    public WebResponse<Boolean> grantJobRole(@Validated @RequestBody GrantJobRoleRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return WebResponse.failWithMsg(bindingResult.getFieldError().getDefaultMessage());
        }
        Long userId = ruyuanGatewayFacade.getCurrentUser().getUserId();
        // 查询当前岗位所有角色
        List<Role> currentUserRoles = roleDomainService.queryJobRole(request.getJobId());
        List<Long> currentUserRoleIds = currentUserRoles.stream().map(Role::getId).collect(Collectors.toList());
        // 删除岗位角色
        List<Long> delIds = currentUserRoleIds.stream().filter(id -> !request.getRoleIdList().contains(id)).collect(Collectors.toList());
        ChangeJobRoleParam delParam = new ChangeJobRoleParam();
        delParam.setOperator(String.valueOf(userId));
        delParam.setJobId(request.getJobId());
        delParam.setRoleIdList(delIds);
        roleDomainService.removeJobRole(delParam);
        // 新增岗位角色
        List<Long> addIds = request.getRoleIdList().stream().filter(roleId -> !currentUserRoleIds.contains(roleId)).collect(Collectors.toList());
        ChangeJobRoleParam addParam = new ChangeJobRoleParam();
        addParam.setOperator(String.valueOf(userId));
        addParam.setJobId(request.getJobId());
        addParam.setRoleIdList(addIds);
        roleDomainService.addJobRole(addParam);
        return WebResponse.successWithData(true);
    }

    //查询用户拥有的角色
    @GetMapping("/userRoleList")
    @ResponseBody
    public WebResponse<List<RoleListVO>> queryUserRoleList(Long userId) {
        List<Role> roles = roleDomainService.queryUserRole(userId);
        return WebResponse.successWithData(roles.stream().map(converter::convertModelToListVO).collect(Collectors.toList()));
    }

    //查询岗位拥有的角色
    @GetMapping("/jobRoleList")
    @ResponseBody
    public WebResponse<List<RoleListVO>> queryJobRoleList(Long jobId) {
        List<Role> roles = roleDomainService.queryJobRole(jobId);
        return WebResponse.successWithData(roles.stream().map(converter::convertModelToListVO).collect(Collectors.toList()));
    }

}
